<?php

class XQL_ObjectSchema
{
	/**
	 * A séma neve
	 *
	 * @var string
	 */
	protected $name = 'default';
	
	/**
	 * Jelzi, hogy a séma ezen eleme kötelező-e vagy elhagyható.
	 *
	 * @var bool
	 */
	protected $isRequired = FALSE;
	
	/**
	 * Jelzi, hogy a séma ezen eleme alá tartozhatnak-e további elemek.
	 *
	 * @var bool
	 */
	protected $isContainer = FALSE;
	
	/**
	 * Ebben a tömbben azoknak az osztályoknak a nevei szerepelnek,
	 * amelyek (és amelyek gyermekei) csatolhatóak a sémához.
	 *
	 * @var array
	 */
	protected $acceptedSubSchemas = array();
	
	/**
	 * A csatolt sémák listája.
	 *
	 * @var array
	 */
	protected $joinedSubSchemas = array();
	
	/**
	 * A séma formátuma. A formátum tartalmazhat egy '$content' helyőrzőt,
	 * amely helyére kerül egymás után, szóközzel elválasztva az alsémák
	 * összességének szerkezeti azonosítója. Ha a helyőrző nem létezik,
	 * akkor a string végére, szóközzel elválasztva kerülnek a szerkezeti
	 * azonosítók.
	 *
	 * @var string
	 */
	protected $format = '';
	
	/**
	 * Egyedi azonosító, a szerkezeti forma generálása alatt védi
	 * a szerkezeti azonosítókat az esetleges felülírásoktól.
	 *
	 * @var string
	 */
	protected $id = '';
	
	/**
	 * Konstruktor.
	 *
	 */
	public function __construct()
	{
		// nope
	}
	
	/**
	 * Visszatér egy példánnyal az osztályból.
	 *
	 * @return XQL_ObjectSchema
	 */
	public static function create()
	{
		return new XQL_ObjectSchema();
	}
	
	/**
	 * Beállítja, hogy a séma kötelező-e vagy sem.
	 *
	 * @param bool $value
	 * @return XQL_ObjectSchema Ez a séma objektum.
	 */
	public function &setRequired($value)
	{
		$this->isRequired = $value;
		return $this;
	}
	
	/**
	 * Csatlakoztat egy sémát ehhez a sémához. A csatlakoztatott sémának
	 * olyan osztály példányának kell lennie, amelyet ez a séma elfogad.
	 * Ha a csatolni ívánt objektum nem séma, XQL_E_ObjectSchema_InvalidJoin kivétel
	 * generálódik.
	 * Egy séma csak egyszer kerülhet csatlakoztatásra ugyanabban a sémának
	 * ugyanazon a szintjén.
	 *
	 * @param XQL_ObjectSchema $schema
	 * @return XQL_ObjectSchema
	 */
	public function &join($schema)
	{
		if ($this->validateJoin($schema))
		{
			$this->joinedSubSchemas[$schema->name] = $schema;
		}
		else
		{
			throw new XQL_E_ObjectSchema_InvalidJoin(get_class($this), get_class($schema));
		}
		
		return $this;
	}
	
	/**
	 * Ellenőrzi a séma csatlakoztatás helyességét. Ha ehhez a sémához nem
	 * lehetséges sémák csatolása, vagy a csatolni kívánt séma nem engedélyezett,
	 * akkor XQL_E_ObjectSchema_IsNotContainer kivétel generálódik.
	 *
	 * @param XQL_ObjectSchema $schema
	 * @return bool Sikeresség esetén TRUE.
	 */
	protected function validateJoin($schema)
	{
		if (!$this->isContainer)
		{
			throw new XQL_E_ObjectSchema_IsNotContainer(get_class($this));
		}
		
		if ($schema instanceof XQL_ObjectSchema)
		{
			if (in_array(get_class($schema), $this->acceptedSubSchemas))
			{
				return TRUE;
			}
			else
			{
				return FALSE;
			}
		}
		else
		{
			throw new XQL_E_ObjectSchema_InstanceError();
		}
	}
	
	/**
	 * Ellenőrzi a séma alapján, hogy az objektum csatolható-e ahhoz az objektumhoz,
	 * amely a sémát tartalmazza.
	 *
	 * @param XQL_Object $object
	 * @return bool Sikeresség esetén TRUE.
	 */
	public function validateObject($object)
	{
		if ($object instanceof XQL_Object)
		{
			if (!$this->isContainer)
			{
				return FALSE;
			}
			
			if (isset($this->joinedSubSchemas[$object->getObjectName()]))
			{
				return TRUE;
			}
			else
			{
				return FALSE;
			}
		}
		else
		{
			return FALSE;
		}
	}
	
	/**
	 * Beállítja az $id tulajdonság értékét.
	 *
	 * @param string $id
	 */
	public function setId($id)
	{
		$this->id = $id;
	}
	
	/**
	 * Visszatér egy alsémával.
	 *
	 * @param string $name Az alséma neve
	 * @return XQL_ObjectSchema
	 */
	public function get($name)
	{
		return (isset($this->joinedSubSchemas[$name]) ? $this->joinedSubSchemas[$name] : NULL);
	}
	
	public function getName()
	{
		return $this->name;
	}
	
	public function getSubSchemasName()
	{
		$result = array();
		
		if (count($this->joinedSubSchemas) > 0)
		{
			foreach ($this->joinedSubSchemas as $schema)
			{
				$result[] = $schema->getName();
			}
		}
		
		return $result;
	}
	
	/**
	 * Felépíti a séma szerkezeti azonosítóját.
	 *
	 * @return string
	 */
	protected function build()
	{
		$format = $this->format;
		$result = '{'.$this->name.'#'.$this->id.'}';
		
		if ($format != '')
		{
			if (strpos($format, '$content') !== FALSE)
			{
				$result = str_replace('$content', $result, $format);
			}
			else
			{
				$result = $format.' '.$result;
			}
		}
		
		return $result;
	}
	
	/**
	 * Visszatér a séma szerkezeti formájával.
	 *
	 * @return string
	 */
	public function __toString()
	{
		$result = $this->build();
		
		return $result;
	}
}

?>